(function() {
    'use strict';

    const ready = callback => {
        // DOMContentLoaded

        document.addEventListener('DOMContentLoaded', function fn() {
            document.removeEventListener('DOMContentLoaded', fn); // 移除事件
            callback(); // 真实回调
        });
    }



    // init 是工厂函数 返回一个Z的实例对象
    const init = (selector, context) => {
        if (typeof selector === 'function') {
            // 当第一个参数是函数时 调用就绪事件
            ready(selector);
            return;
        }

        return new Z(selector, context);
    }


    // class Z 创建的实例  叫做 Z 对象
    // $(domElement)  将HTML元素转换成 Z对象
    class Z extends Array {
        constructor(selector, context) {
            let elms;

            // 将数组或NodeList(节点集合) 转换成Z的实例
            if (Array.isArray(selector) || selector.__proto__.constructor.name === 'NodeList') {
                super(...selector);
                return;
            }

            if (selector.nodeType === 1) { // 判断参数是否是一个html元素
                super(selector);
            } else {
                // 如果传入了 context 上下文对象 则在这个上下文中查找元素  否则在根节点查找元素
                elms = context ? context.querySelectorAll(selector) : document.querySelectorAll(selector); // 通过选择器选择元素
                super(...elms); // 将选择到的元素转成数组
            }
        }

        on(eventType, callback) {
            if (typeof eventType === 'string' && typeof callback === 'function') {
                this.forEach(elm => { // 遍历所有被选择到的元素
                    elm.addEventListener(eventType, callback); // 为每一个元素添加事件
                });
            } else if (eventType.constructor.name === 'Object') {
                for (let key in eventType) { // 遍历对象 获得key和value 此时key是事件类型 value是事件处理函数
                    this.forEach(elm => {
                        elm.addEventListener(key, eventType[key]);
                    });
                }
            }
        }

        css(style, value) {
            if (typeof style === 'string' && typeof value === 'string') {
                this.forEach(elm => {
                    elm.style[style] = value;
                });
            } else if (style.constructor.name === 'Object') {
                for (let key in style) {
                    this.forEach(elm => {
                        elm.style[key] = style[key];
                    })
                }
            }

            return this; // 返回当前示例 支持链式调用(可以继续调用公有函数)
        }

        addClass(className) {
            if (typeof className === 'string') {
                this.forEach(elm => {
                    elm.classList.add(className);
                });
            }
            return this;
        }

        removeClass(className) {
            if (typeof className === 'string') {
                this.forEach(elm => {
                    elm.classList.remove(className);
                })
            }
            return this;
        }

        toggleClass(className) {
            if (typeof className === 'string') {
                this.forEach(elm => {
                    elm.classList.toggle(className);
                });
            }
            return this;
        }

        replaceClass(oldClass, newClass) {
            if (typeof oldClass === 'string' && typeof newClass === 'string') {
                this.forEach(elm => {
                    elm.classList.replace(oldClass, newClass);
                });
            }
            return this;
        }

        // ---------------------
        // 属性操作
        attr(attrname, attrval) {
            if (typeof attrname === 'string') {
                switch (typeof attrval) {
                    case 'undefined':
                        return this[0].getAttribute(attrname);
                    case 'string':
                        this.forEach(elm => {
                            elm.setAttribute(attrname, attrval);
                        });
                        break;
                    case 'function':
                        this.forEach((elm, i) => {
                            // 调用回调时 传入了 元素的属性值 和 索引值
                            let result = attrval(elm.getAttribute(attrname), i);
                            elm.setAttribute(attrname, result);
                        });
                        break;
                }
            }
        }


        removeAttr(attrname) {
            if (typeof attrname === 'string' && attrname) {
                this.forEach(elm => {
                    if (elm.hasAttribute(attrname)) {
                        elm.removeAttribute(attrname);
                    }
                });
            }
            return this;
        }

        // -----------------
        // 文档查找
        find(selector) {
            if (typeof selector === 'string' && selector) {
                let nodeList = []; // 存放结果 所有被选元素的元素的指定后代元素

                this.forEach(el => {
                    // 在被选元素中进行元素查找
                    // 将结果存放进数组中
                    nodeList.push(...el.querySelectorAll(selector));
                });

                nodeList = [...new Set(nodeList)]; // 进行去重

                // 将数组包装成Z的实例并返回
                return init(nodeList);
            }
        }

        eq(index) {
            if (typeof index === 'number' && index === parseInt(index) && index >= 0) {
                return init(this[index]);
            }
        }

        children(selector) {
            let result = [];
            this.forEach(el => { // 遍历所有的被选元素
                let domList;

                if (typeof selector === 'string' && selector) {
                    domList = Array.from(el.querySelectorAll(selector)); // 转换成数组 当前元素的后代元素
                    // 将上面的结果中每一个元素的 父节点 拿出来 和 el比较相等
                    // 如果父节点和el相等 说明 这个节点就是el的子节点
                    domList = domList.filter(elm => elm.parentNode === el);


                } else {
                    // 直接获得所有子元素
                    domList = Array.from(el.children);
                }

                result.push(...domList);
            });

            return init(result);
        }

        siblings(selector) {
            let result = []; // 存放筛选结果

            this.forEach(elm => {
                let sibling = Array.from(elm.parentNode.children); // 包含了自身的 所有兄弟元素
                let _index = sibling.findIndex(el => el === elm); // 找到元素自身 在数组中的索引
                sibling.splice(_index, 1);

                if (typeof selector === 'string' && selector) {
                    // 通过选择器 在目标的父元素中 查找的是每一个元素的后代元素
                    let selected = Array.from(elm.parentNode.querySelectorAll(selector));
                    // 如果两个数组中都出现了的元素 就是它的兄弟元素
                    // 在两个数组中查到相同元素
                    sibling = sibling.filter(el => selected.indexOf(el) > -1);
                }

                result.push(...sibling);
            });

            result = [...new Set(result)];

            return init(result);
        }

        index(elm) {
            return this.findIndex(el => el === elm);
        }

        tabs(options) {
            const defaults = {
                ev: 'click',
                active: 'active',
                display: 'display',
                nodisplay:'none'
            };

            Object.assign(defaults, options); // 合并对象

            // 选择元素
            const btns = this.find('[data-type="tabs-btn"]');
            const divs = this.find('[data-type="tabs-display"]');

            btns.on(defaults.ev, function() {
                let _index = btns.index(this);
                $(this).addClass(defaults.active).siblings().removeClass(defaults.active);
                divs.eq(_index).addClass(defaults.display).siblings().removeClass(defaults.display);
            });
            btns.on('mouseout', function() {
                let _index = btns.index(this);
                divs.eq(_index).removeClass(defaults.display);
            })

        }

        // --------------
        // 内容和值
        html(htmlContent) {
            switch (typeof htmlContent) {
                case 'undefined':
                    return this[0].innerHTML;
                case 'string':
                    this.forEach(el => {
                        el.innerHTML = htmlContent;
                    });
                    break;
                case 'function':
                    this.forEach((el, i) => {
                        el.innerHTML = htmlContent(el.innerHTML, i);
                    });
                    break;
            }
        }

        val(value) {
            switch (typeof value) {
                case 'undefined':
                    return this[0].value;
                case 'string':
                    this.forEach(el => {
                        el.value = value;
                    });
                    break;
                case 'function':
                    this.forEach((el, i) => {
                        el.value = value(el.value, i);
                    });
                    break;
            }
        }
    }


    window.$ = init;
})();